////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek Studios, 2008.
// -------------------------------------------------------------------------
//  File name:   TerrainManager.cpp
//  Created:     30/3/2009 by Paulo Zaffari.
//  Description: Manages layer and heightfield data.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include <StdAfx.h>
#include "TerrainManager.h"

//////////////////////////////////////////////////////////////////////////
#include "SurfaceType.h"
#include "Layer.h"
#include "TerrainTexGen.h"
#include "..\Util\AutoLogTime.h"
#include "..\OBJExporter1.h"

#include "..\GameEngine.h"

#include "SegmentedWorld/SegmentedWorldManager.h"

//////////////////////////////////////////////////////////////////////////

static CTerrainManager* s_oTerrainManager = NULL;

//////////////////////////////////////////////////////////////////////////
CTerrainManager&	CTerrainManager::GetTerrainManager()
{
	if (!s_oTerrainManager)
		s_oTerrainManager = new CTerrainManager;
	return *s_oTerrainManager;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainManager::ShutDown()
{
	delete s_oTerrainManager;
	s_oTerrainManager = 0;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainManager::RemoveSurfaceType( int i )
{
	GetIEditor()->SetModifiedFlag(TRUE);
	GetIEditor()->SetModifiedModule(eModifiedTerrain);
	m_surfaceTypes.erase( m_surfaceTypes.begin()+i );
	ConsolidateSurfaceTypes();
}

//////////////////////////////////////////////////////////////////////////
void CTerrainManager::RemoveSurfaceType( CSurfaceType *pSurfaceType )
{
	for (int i = 0; i < m_surfaceTypes.size(); i++)
	{
		if (m_surfaceTypes[i] == pSurfaceType)
		{
			m_surfaceTypes.erase( m_surfaceTypes.begin()+i );
			break;
		}
	}
	ConsolidateSurfaceTypes();
}

//////////////////////////////////////////////////////////////////////////
int CTerrainManager::AddSurfaceType( CSurfaceType *srf )
{
	GetIEditor()->SetModifiedFlag(TRUE);
	GetIEditor()->SetModifiedModule(eModifiedTerrain);
	m_surfaceTypes.push_back(srf);

	return m_surfaceTypes.size()-1;
}

//////////////////////////////////////////////////////////////////////////
void CTerrainManager::RemoveAllSurfaceTypes()
{
	GetIEditor()->SetModifiedFlag(TRUE);
	GetIEditor()->SetModifiedModule(eModifiedTerrain);
	m_surfaceTypes.clear();
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::SerializeSurfaceTypes( CXmlArchive &xmlAr, bool bUpdateEngineTerrain )
{
	if (xmlAr.bLoading)
	{
		// Clear old layers
		RemoveAllSurfaceTypes();

		// Load the layer count
		XmlNodeRef node = xmlAr.root->findChild( "SurfaceTypes" );
		if (!node)
			return;

		// Read all node
		for (int i=0; i < node->getChildCount(); i++)
		{
			CXmlArchive ar( xmlAr );
			ar.root = node->getChild(i);
			CSurfaceType *sf = new CSurfaceType;
			sf->Serialize( ar );

			if (sf->GetSurfaceTypeID() < 0) // For older levels.
			{
				sf->SetSurfaceTypeID(i);
			}

			AddSurfaceType( sf );
		}
		ReloadSurfaceTypes(bUpdateEngineTerrain);
	}
	else
	{
		// Storing
		CLogFile::WriteLine("Storing surface types...");

		// Save the layer count

		XmlNodeRef node = xmlAr.root->newChild( "SurfaceTypes" );

		// Write all surface types.
		for (int i = 0; i < m_surfaceTypes.size(); i++)
		{
			CXmlArchive ar( xmlAr );
			ar.root = node->newChild( "SurfaceType" );
			m_surfaceTypes[i]->Serialize( ar );
		}
	}
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::ConsolidateSurfaceTypes()
{
	// We must consolidate the IDs after removing 
	for (size_t i = 0; i < m_surfaceTypes.size(); ++i)
	{
		m_surfaceTypes[i]->SetSurfaceTypeID(i);
	}
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::ReloadSurfaceTypes(bool bUpdateEngineTerrain, bool bUpdateHeightmap)
{
  LOADING_TIME_PROFILE_SECTION(gEnv->pSystem);

	CXmlArchive	ar;
	XmlNodeRef node = XmlHelpers::CreateXmlNode( "SurfaceTypes" );
	// Write all surface types.
	for (int i = 0; i < GetSurfaceTypeCount(); i++)
	{
		ar.root = node->newChild( "SurfaceType" );
		GetSurfaceTypePtr(i)->Serialize( ar );
	}

  // temporary console variable for world segmentation testing
  //static ICVar * pWSeg = GetIEditor()->GetSystem()->GetIConsole()->GetCVar("e_WorldSegmentationTest");

  //if(pWSeg && pWSeg->GetIVal()>1)
  //{
  //  int nWorldGridDim = pWSeg->GetFVal();

  //  // pass same surface types list to all segments
  //  for(int nSID=0; nSID < nWorldGridDim*nWorldGridDim; nSID++)
  //    gEnv->p3DEngine->LoadTerrainSurfacesFromXML(node, bUpdateEngineTerrain, nSID);
  //}
  //else
    gEnv->p3DEngine->LoadTerrainSurfacesFromXML(node, bUpdateEngineTerrain);

	if (bUpdateHeightmap && gEnv->p3DEngine->GetITerrain() && (bUpdateEngineTerrain || !CSWManager::Get().IsSWDoc()))
	{
		m_cHeightmap.UpdateEngineTerrain(false,false);
	}
}
//////////////////////////////////////////////////////////////////////////
int CTerrainManager::FindSurfaceType( const CString &name )
{
	for (int i = 0; i < m_surfaceTypes.size(); i++)
	{
		if (stricmp(m_surfaceTypes[i]->GetName(),name) == 0)
			return i;
	}
	return -1;
}
//////////////////////////////////////////////////////////////////////////












//////////////////////////////////////////////////////////////////////////
void CTerrainManager::RemoveLayer( int layer )
{
	assert( layer >= 0 && layer < m_layers.size() );

	delete m_layers[layer];
	m_layers.erase( m_layers.begin() + layer );
}

//////////////////////////////////////////////////////////////////////////
CLayer* CTerrainManager::FindLayer( const char *sLayerName ) const
{
	for (int i = 0; i < GetLayerCount(); i++)
	{
		CLayer *layer = GetLayer(i);
		if (strcmp(layer->GetLayerName(),sLayerName) == 0)
			return layer;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
CLayer* CTerrainManager::FindLayerByLayerId( const uint32 dwLayerId ) const
{
	for (int i = 0; i < GetLayerCount(); i++)
	{
		CLayer *layer = GetLayer(i);
		if (layer->GetCurrentLayerId() == dwLayerId)
			return layer;
	}
	return 0;
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::RemoveLayer( CLayer *layer )
{
	if (layer)
	{
		GetHeightmap().RemoveLayerID(layer->GetCurrentLayerId());

		delete layer;
		m_layers.erase( std::remove(m_layers.begin(),m_layers.end(),layer),m_layers.end() );
	}
}

//////////////////////////////////////////////////////////////////////////
void CTerrainManager::SwapLayers( int layer1,int layer2 )
{
	assert( layer1 >= 0 && layer1 < m_layers.size() );
	assert( layer2 >= 0 && layer2 < m_layers.size() );
	std::swap( m_layers[layer1],m_layers[layer2] );

	// If necessary, also swap the surface types (their order is important when using masked surface type transitions)
	int st1 = m_layers[layer1]->GetEngineSurfaceTypeId();
	int st2 = m_layers[layer2]->GetEngineSurfaceTypeId();
	CSurfaceType *pSurfaceType1 = GetSurfaceTypePtr(st1);
	CSurfaceType *pSurfaceType2 = GetSurfaceTypePtr(st2);
	if (pSurfaceType1 && pSurfaceType2 && sgn(st1 - st2) == -sgn(layer1 - layer2))
	{
		CLayer TempLayer;
		TempLayer.SetSurfaceType(pSurfaceType1); // Temporarily holds the surface type so that it does not get deleted
		m_layers[layer1]->SetSurfaceType(pSurfaceType2);
		m_layers[layer2]->SetSurfaceType(pSurfaceType1);
		TempLayer.SetSurfaceType(NULL);
		CSurfaceType &surfaceType1 = *m_surfaceTypes[st1];
		CSurfaceType &surfaceType2 = *m_surfaceTypes[st2];
		std::swap(surfaceType1, surfaceType2);
		m_cHeightmap.UpdateEngineTerrain();
	}
}

//////////////////////////////////////////////////////////////////////////
void CTerrainManager::InvalidateLayers()
{
	////////////////////////////////////////////////////////////////////////
	// Set the update needed flag for all layer
	////////////////////////////////////////////////////////////////////////

	unsigned int i;

	// Free the layer objects
	for (i=0; i< GetLayerCount(); i++)
		GetLayer(i)->InvalidateMask();
}
//////////////////////////////////////////////////////////////////////////








//////////////////////////////////////////////////////////////////////////
void CTerrainManager::ClearLayers()
{
	////////////////////////////////////////////////////////////////////////
	// Clear all texture layers
	////////////////////////////////////////////////////////////////////////
	unsigned int i;

	// Free the layer objects
	for (i=0; i<GetLayerCount(); i++)
		delete GetLayer(i);

	// Clear the list
	m_layers.clear();
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::SerializeLayerSettings( CXmlArchive &xmlAr )
{
	////////////////////////////////////////////////////////////////////////
	// Load or restore the layer settings from an XML
	////////////////////////////////////////////////////////////////////////
	if (xmlAr.bLoading)
	{
		// Loading

		CLogFile::WriteLine("Loading layer settings...");
		CWaitProgress progress( _T("Loading Terrain Layers") );

		// Clear old layers
		ClearLayers();

		// Load the layer count
		XmlNodeRef layers = xmlAr.root->findChild( "Layers" );
		if (!layers)
			return;

		// Read all layers
		int numLayers = layers->getChildCount();
		for (int i = 0; i < numLayers; i++)
		{
			progress.Step( 100*i/numLayers );
			// Create a new layer
			AddLayer( new CLayer );

			CXmlArchive ar( xmlAr );
			ar.root = layers->getChild(i);
			// Fill the layer with the data
			GetLayer(i)->Serialize( ar );

			CryLog("  loaded editor layer %d  name='%s' LayerID=%d",i,GetLayer(i)->GetLayerName(),GetLayer(i)->GetCurrentLayerId());
		}

		// If surface type ids are unassigned, assign them.
		for (int i = 0; i < GetSurfaceTypeCount(); i++)
		{
			if (GetSurfaceTypePtr(i)->GetSurfaceTypeID() < 0 || GetSurfaceTypePtr(i)->GetSurfaceTypeID() >= MAX_SURFACE_TYPE_ID_COUNT)
				GetSurfaceTypePtr(i)->AssignUnusedSurfaceTypeID();
		}
	}
	else
	{
		// Storing

		CLogFile::WriteLine("Storing layer settings...");

		// Save the layer count

		XmlNodeRef layers = xmlAr.root->newChild( "Layers" );

		// Write all layers
		for (int i=0; i < GetLayerCount(); i++)
		{
			CXmlArchive ar( xmlAr );
			ar.root = layers->newChild( "Layer" );;
			GetLayer(i)->Serialize( ar );
		}
	}
}
//////////////////////////////////////////////////////////////////////////
uint32 CTerrainManager::GetDetailIdLayerFromLayerId( const uint32 dwLayerId )
{
	for (int i = 0,num = (int)m_layers.size(); i < num; i++)
	{
		CLayer &rLayer = *(m_layers[i]);

		if(dwLayerId==rLayer.GetOrRequestLayerId())
		{
			return rLayer.GetEngineSurfaceTypeId();
		}
	}

	// recreate referenced layer
	if(!GetIEditor()->GetSystem()->GetI3DEngine()->GetITerrain())		// only if terrain loaded successfully
	{
		CString no;

		no.Format("%d",dwLayerId);

		CLayer *pNewLayer = new CLayer;

		pNewLayer->SetLayerName(CString("LAYER_")+no);
		pNewLayer->SetLayerId(dwLayerId);

		AddLayer(pNewLayer);
	}

	return 0xffffffff;	
}
//////////////////////////////////////////////////////////////////////////
uint32 CTerrainManager::GetLayerIdFromDetailLayerId( const uint32 _dwDetailLayerId )
{
	uint32 dwDetailLayerId=_dwDetailLayerId;

	CSurfaceType *pSurfaceType = 0;

	for (int i = 0; i < (int)m_surfaceTypes.size(); i++)
	{
		if (m_surfaceTypes[i]->GetSurfaceTypeID() == _dwDetailLayerId)
		{
			pSurfaceType = m_surfaceTypes[i];
			break;
		}
	}

	if(!pSurfaceType)
	{
		// recreate referenced surfacetype

		pSurfaceType = new CSurfaceType;

		CString no;

		no.Format("%d",dwDetailLayerId);

		pSurfaceType->SetName(CString("SURFACETYPE_")+no);
		pSurfaceType->SetSurfaceTypeID(_dwDetailLayerId);
		AddSurfaceType(pSurfaceType);
	}

	std::vector<CLayer*>::iterator it, end=m_layers.end();

	for(it=m_layers.begin();it!=end;++it)
	{
		CLayer &rLayer = *(*it);

		if(rLayer.GetEngineSurfaceTypeId()==dwDetailLayerId)
			return rLayer.GetOrRequestLayerId();
	}

	// recreate referenced layer

	CLayer *pNewLayer = new CLayer;

	uint32 dwRet = pNewLayer->GetOrRequestLayerId();		// to create new LayerId

	assert(dwRet!=0xffffffff);

	//pNewLayer->SetSurfaceTypeId(dwDetailLayerId);
	pNewLayer->SetLayerName(CString("DETAILLAYER_")+pSurfaceType->GetName());

	pNewLayer->SetSurfaceType(pSurfaceType);

	AddLayer(pNewLayer);

	return dwRet;
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::MarkUsedLayerIds( bool bFree[256] ) const
{
	std::vector<CLayer*>::const_iterator it;

	for(it=m_layers.begin();it!=m_layers.end();++it)
	{
		CLayer *pLayer=*it;

		if(pLayer->GetCurrentLayerId()!=0xffffffff)
			bFree[pLayer->GetCurrentLayerId()]=false;
	}
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::CreateDefaultLayer()
{
	// Create default layer.
	CLayer *layer = new CLayer;
	AddLayer( layer );
	layer->SetLayerName( "Default" );
	layer->LoadTexture( Path::GetGameFolder()+"/Textures/defaults/defaultnouvs.dds" );
	layer->SetLayerId(0);

	// Create default surface type.
	CSurfaceType *sfType = new CSurfaceType;
	sfType->SetName( "Materials/material_terrain_default" );
	uint32 dwDetailLayerId = AddSurfaceType( sfType );
	sfType->SetMaterial("Materials/material_terrain_default");
	sfType->AssignUnusedSurfaceTypeID();

	layer->SetSurfaceType(sfType);

	m_bIsNewTerranTextureSystem=true;
}
//////////////////////////////////////////////////////////////////////////
//void CTerrainManager::SerializeTerrain(CXmlArchive& xmlAr,char* szLevelPath,char* currentMissionName,bool bReloadEngineLevel)
void CTerrainManager::SerializeTerrain(TDocMultiArchive &arrXmlAr,char* szLevelPath,char* currentMissionName,bool bReloadEngineLevel)
{
	bool bLoading = false;
	bool bFound = false;
	for (int i = 0; i < _countof(arrXmlAr); i++)
	{
		if (arrXmlAr[i] != 0)
		{
			bLoading = (*arrXmlAr[i]).bLoading;
			bFound = true;
			break;
		}
	}

	if (!bFound)
		return;


	if (bLoading)
	{
		
		m_cHeightmap.Serialize((*arrXmlAr[sw::WDB_LEVELGENERAL]),false,false);

		if (bReloadEngineLevel)
		{
			CAutoLogTime logtime( "3D Engine level load" );
			GetIEditor()->GetGameEngine()->LoadLevel( szLevelPath,currentMissionName,true,true,false );
		}

		//////////////////////////////////////////////////////////////////////////
		// Load terrain heightmap data
		//////////////////////////////////////////////////////////////////////////

		bool bTerrainRefreshRequested=false;

		// Surface Types ///////////////////////////////////////////////////////

		{
			CAutoLogTime logtime( "Loading Surface Types" );
			SerializeSurfaceTypes( (*arrXmlAr[sw::WDB_TERRAIN_LAYERS]) );
		}	

		// Terrain texture /////////////////////////////////////////////////////
		{
			CAutoLogTime logtime( "Loading Terrain Layers Info" );
			SerializeLayerSettings((*arrXmlAr[sw::WDB_TERRAIN_LAYERS]));
		}

		{
			CAutoLogTime logtime( "Load Terrain" );

			if (!CSWManager::Get().IsSWDoc())
				m_cHeightmap.SerializeTerrain( (*arrXmlAr[sw::WDB_LEVELGENERAL]), bTerrainRefreshRequested );
			else {
				GetIEditor()->Get3DEngine()->DeleteTerrain();
				GetIEditor()->GetGameEngine()->InitTerrain();
			}

			{
				XmlNodeRef layers = (*arrXmlAr[sw::WDB_LEVELGENERAL]).root->findChild( "Layers" );
				if (layers)
				{
					int numLayers = layers->getChildCount();
					for (int i = 0; i < numLayers; i++)
					{
						GetLayer(i)->Update3dengineInfo();
					}
				}
			}
		}

		//if (!CSWManager::Get())
		{
			CAutoLogTime logtime( "Load Vegetation" );
			m_cHeightmap.SerializeVegetation((*arrXmlAr[sw::WDB_VEGETATION]));
		}

		{
			CAutoLogTime logtime( "Terrain Postprocess" );
			m_cHeightmap.ProcessAfterLoading();		// after loading layers and surfacetypes
		}


		if (!CSWManager::Get().IsSWDoc())
		{
			CAutoLogTime logtime( "Process RGB Terrain Layers" );
			ConvertLayersToRGBLayer();
		}

		// Update terrain in engine.
		if (!CSWManager::Get().IsSWDoc())
			m_cHeightmap.UpdateEngineTerrain();
	}
	else
	{
		// save terrain heightmap data
		bool bTerrainRefreshRequested(false);

		if (arrXmlAr[sw::WDB_LEVELGENERAL] != NULL)
		{


			// Heightmap ///////////////////////////////////////////////////////////
			(*arrXmlAr[sw::WDB_LEVELGENERAL]).root->setAttr( "HeightmapWidth",m_cHeightmap.GetWidth() );
			(*arrXmlAr[sw::WDB_LEVELGENERAL]).root->setAttr( "HeightmapHeight",m_cHeightmap.GetHeight() );

			m_cHeightmap.Serialize( (*arrXmlAr[sw::WDB_LEVELGENERAL]),true,false );

			if (!CSWManager::Get().IsSWDoc())
				m_cHeightmap.SerializeTerrain( (*arrXmlAr[sw::WDB_LEVELGENERAL]), bTerrainRefreshRequested );

			// Terrain texture /////////////////////////////////////////////////////

			// Surface Types ///////////////////////////////////////////////////////
		}

		if (arrXmlAr[sw::WDB_TERRAIN_LAYERS] != NULL)
		{
			//if (!CSWManager::Get())
			{
				SerializeSurfaceTypes( (*arrXmlAr[sw::WDB_TERRAIN_LAYERS]) );

				SerializeLayerSettings((*arrXmlAr[sw::WDB_TERRAIN_LAYERS]));
			}
		}

		if (arrXmlAr[sw::WDB_VEGETATION] != NULL)
		{
			//if (!CSWManager::Get())
			{
				m_cHeightmap.SerializeVegetation((*arrXmlAr[sw::WDB_VEGETATION]));
			}
		}
	}
}
//////////////////////////////////////////////////////////////////////////
bool	CTerrainManager::ExportTerrainAsGeometry(const char *pszFileName, RECT rcExport)
{
	////////////////////////////////////////////////////////////////////////
	// Store the terrain in the OBJ format on the disk
	////////////////////////////////////////////////////////////////////////

	t_hmap *pHeigthmap = NULL;
	COBJExporter cModel;
	CVector3D vVertex[4];
	CTexCoord2D cTexCoord[4];

	int iWidth = m_cHeightmap.GetWidth();
	CFace sNewFace;
	CFace sNewFace2;
	char szInfoPath[_MAX_PATH];
	const int iSurfaceTexWidth = 4096;
	DWORD *pSurface = NULL;
	float fScale = (1.0f / m_cHeightmap.GetWidth()) * (float) iSurfaceTexWidth;

	if ((int)rcExport.bottom - rcExport.top <= 0 && (int)rcExport.right - rcExport.left <= 0)
		return false;

	cModel.StartNewObject("Terrain");
	cModel.StartNewSubMesh();

	// Get data from the heightmap
	pHeigthmap = m_cHeightmap.GetData();
	ASSERT(pHeigthmap);

	// Adjust the rectangle to be valid
	if (rcExport.right < 0)
		rcExport.right = 0;
	if (rcExport.left < 0)
		rcExport.left = 0;
	if (rcExport.top < 0)
		rcExport.top = 0;
	if (rcExport.bottom < 0)
		rcExport.bottom = 0;

	if (rcExport.right >= m_cHeightmap.GetWidth())
		rcExport.right = m_cHeightmap.GetWidth() - 1;
	if (rcExport.bottom >= m_cHeightmap.GetHeight())
		rcExport.bottom = m_cHeightmap.GetHeight() - 1;

	////////////////////////////////////////////////////////////////////////
	// Create triangles, texture coordinates and indices
	////////////////////////////////////////////////////////////////////////

	CWaitProgress progress("Export Terrain to .OBJ");
	int progressUpdate = 0;

	// scale the same like for objects
	float fExScale = 100.0f;
	float fUnitExScale = m_cHeightmap.GetUnitSize() * fExScale;
	float fExportWidth = float(rcExport.right - rcExport.left);
	float fExportHeight = float(rcExport.bottom - rcExport.top);

	for (int y=rcExport.top; y<rcExport.bottom; ++y)
	{
		float dy = y;

		for (int x=rcExport.left; x<rcExport.right; ++x)
		{
			float dx = x;

			// Quad vertices
			vVertex[0].fY = dx * fUnitExScale;
			vVertex[0].fX = dy * fUnitExScale;
			vVertex[0].fZ = pHeigthmap[x + y * iWidth] * fExScale;

			vVertex[1].fY = dx * fUnitExScale;
			vVertex[1].fX = (dy + 1) * fUnitExScale;
			vVertex[1].fZ = pHeigthmap[x + (y + 1) * iWidth] * fExScale;

			vVertex[2].fY = (dx + 1) * fUnitExScale;
			vVertex[2].fX = dy * fUnitExScale;
			vVertex[2].fZ = pHeigthmap[(x + 1) + y * iWidth] * fExScale;

			vVertex[3].fY = (dx + 1) * fUnitExScale;
			vVertex[3].fX = (dy + 1) * fUnitExScale;
			vVertex[3].fZ = pHeigthmap[(x + 1) + (y + 1) * iWidth] * fExScale;


			// Quad texture coords
			cTexCoord[0].fU = dx / fExportWidth;
			cTexCoord[0].fV = -dy / fExportHeight;

			cTexCoord[1].fU = dx / fExportWidth;
			cTexCoord[1].fV = -(dy + 1) / fExportHeight;

			cTexCoord[2].fU = (dx + 1) / fExportWidth;
			cTexCoord[2].fV = -dy / fExportHeight;

			cTexCoord[3].fU = (dx + 1) / fExportWidth;
			cTexCoord[3].fV = -(dy + 1) / fExportHeight;


			// Add the vertices to the model and store the indices in the face
			sNewFace.iVertexIndices[0] = cModel.GetVertexIdx(&vVertex[0]);
			sNewFace.iVertexIndices[1] = cModel.GetVertexIdx(&vVertex[1]);
			sNewFace.iVertexIndices[2] = cModel.GetVertexIdx(&vVertex[2]);
			sNewFace.iTexCoordIndices[0] = cModel.GetTexCoordIdx(&cTexCoord[0]);
			sNewFace.iTexCoordIndices[1] = cModel.GetTexCoordIdx(&cTexCoord[1]);
			sNewFace.iTexCoordIndices[2] = cModel.GetTexCoordIdx(&cTexCoord[2]);
			cModel.AddFace(sNewFace);

			sNewFace2.iVertexIndices[0] = sNewFace.iVertexIndices[2];
			sNewFace2.iVertexIndices[1] = sNewFace.iVertexIndices[1];
			sNewFace2.iVertexIndices[2] = cModel.GetVertexIdx(&vVertex[3]);
			sNewFace2.iTexCoordIndices[0] = sNewFace.iTexCoordIndices[2];
			sNewFace2.iTexCoordIndices[1] = sNewFace.iTexCoordIndices[1];
			sNewFace2.iTexCoordIndices[2] = cModel.GetTexCoordIdx(&cTexCoord[3]);
			cModel.AddFace(sNewFace2);
		}

		if(!(progressUpdate%10))
		{
			if ( !progress.Step(100 * (y-rcExport.top) / (rcExport.bottom-rcExport.top)) )
			{
				AfxMessageBox("Export Terrain to .OBJ file was skipped by user.");
				return false;
			}
		}
		progressUpdate++;
	}

	CExpMtl mtl;
	strcpy(mtl.m_name, "terrain");
	cModel.AddMtl(mtl);

	////////////////////////////////////////////////////////////////////////
	// Export .OBJ and .MTL files
	////////////////////////////////////////////////////////////////////////

	// Write the model into the file
	if (!cModel.WriteOBJ(pszFileName, false))
	{
		CLogFile::WriteLine("Error while exporting part of the heightmap as OBJ model !");
		AfxMessageBox("Crititcal error during exporting !");
		return false;
	}

	////////////////////////////////////////////////////////////////////////
	// Export Info
	////////////////////////////////////////////////////////////////////////
	{
		strcpy(szInfoPath, pszFileName);
		PathRemoveExtension(szInfoPath);
		strcat(szInfoPath,".inf");
		if (CFileUtil::OverwriteFile( szInfoPath ))
		{
			FILE *infoFile = fopen( szInfoPath,"wt" );
			if (infoFile)
			{
				fprintf( infoFile,"x=%d,y=%d,width=%d,height=%d",rcExport.left,rcExport.top,rcExport.right-rcExport.left,rcExport.bottom-rcExport.top );
				fclose( infoFile );
			}
		}
	}

	/*
	////////////////////////////////////////////////////////////////////////
	// Generate the texture
	////////////////////////////////////////////////////////////////////////

	char szBitmapPath[_MAX_PATH];
	UINT iSrcX, iSrcY, iDestX, iDestY, iSrcWidth, iDestWidth,iDestHeight;

	// Allocate memory and generate the surface
	CImage terrainSurface;
	terrainSurface.Allocate( iSurfaceTexWidth,iSurfaceTexWidth );
	pSurface = (DWORD*)terrainSurface.GetData();
	//cTexture.GenerateSurface(pSurface, iSurfaceTexWidth, iSurfaceTexWidth, GEN_USE_LIGHTING|GEN_STATOBJ_SHADOWS|GEN_SHOW_WATER, NULL );
	CTerrainTexGen texGen;
	texGen.GenerateSurfaceTexture( ETTG_LIGHTING|ETTG_STATOBJ_SHADOWS|ETTG_SHOW_WATER,terrainSurface );

	//CImageUtil::SaveJPEG( "Terrain.jpg",iSurfaceTexWidth,iSurfaceTexWidth,pSurface );

	// Scale the export rectangle to texture coordinates
	rcExport.bottom *= fScale;
	rcExport.left *= fScale;
	rcExport.right *= fScale;
	rcExport.top *= fScale;

	CRect rc1 = rcExport;
	rc1 &= CRect( 0,0,iSurfaceTexWidth,iSurfaceTexWidth );
	rcExport = rc1;

	iDestWidth = rcExport.right - rcExport.left;
	iDestHeight =  rcExport.bottom - rcExport.top;
	iSrcWidth = iSurfaceTexWidth;

	// Allocate memory for the needed portion of the surface
	CImage exportImage;
	exportImage.Allocate( iDestWidth,iDestHeight );

	// Extract the needed portion of the surface
	for (j=rcExport.top; j<rcExport.bottom; j++)
	{
		iSrcX = rcExport.left;
		iSrcY = j;

		iDestX = 0;
		iDestY = j - rcExport.top;

		memcpy( &exportImage.ValueAt(iDestX,iDestY), &pSurface[iSrcX + iSrcY * iSrcWidth], sizeof(DWORD)*iDestWidth );
	}

	////////////////////////////////////////////////////////////////////////
	// Export the texture
	////////////////////////////////////////////////////////////////////////

	strcpy(szBitmapPath, pszFileName);
	PathRemoveFileSpec(szBitmapPath);
	PathAddBackslash(szBitmapPath);
	strcat(szBitmapPath, "Terrain.bmp");

	if (!CImageUtil::SaveImage( szBitmapPath, exportImage ))
		return false;
	*/

	return true;
}
//////////////////////////////////////////////////////////////////////////
void	CTerrainManager::GetTerrainMemoryUsage( ICrySizer *pSizer )
{
	{
		SIZER_COMPONENT_NAME(pSizer,"Layers");

		std::vector<CLayer*>::iterator it;

		for(it=m_layers.begin();it!=m_layers.end();++it)
		{
			CLayer *pLayer=*it;

			pLayer->GetMemoryUsage(pSizer);
		}
	}

	{
		SIZER_COMPONENT_NAME(pSizer,"CHeightmap");

		GetHeightmap().GetMemoryUsage(pSizer);
	}
}
//////////////////////////////////////////////////////////////////////////
bool CTerrainManager::ConvertLayersToRGBLayer()
{
	bool bConvertNeeded = !GetIEditor()->GetHeightmap()->m_TerrainRGBTexture.IsAllocated();

	if(!bConvertNeeded)
		return false;

	std::vector<CLayer*>::iterator it;

	uint32 dwTexResolution=0;

	for(it=m_layers.begin();it!=m_layers.end();++it)
	{
		CLayer *pLayer=*it;

		dwTexResolution = max(dwTexResolution,(uint32)pLayer->GetMaskResolution());
		/*
		if(pLayer->GetMask().IsValid())
		{ 
		dwTexResolution=pLayer->GetMask().GetWidth();
		bLayersStillHaveAMask=true;
		break; 
		}
		*/
	}

	if(AfxMessageBox("Convert LayeredTerrainTexture to RGBTerrainTexture?",MB_YESNO,0)==IDNO)
		return false;

	if(AfxMessageBox("Double the resolution of the existing data?",MB_YESNO,0)==IDYES)
		dwTexResolution*=2;

	// extract RGB texture
	{
		CTerrainLayerTexGen texGen;

		texGen.Init(dwTexResolution);

		SSectorInfo si;

		GetIEditor()->GetHeightmap()->GetSectorsInfo( si );

		uint32 dwNumSectors = si.numSectors;

		uint32 dwSectorTexResolution = 512;
		uint32 dwSectorResolution = dwTexResolution/dwNumSectors; 
		uint32 dwNumTexSectors = dwTexResolution/dwSectorTexResolution; 

		// no error check because this is allocating only very few bytes
		m_cHeightmap.m_TerrainRGBTexture.AllocateTiles(dwNumTexSectors,dwNumTexSectors,dwSectorTexResolution);	// dwTexResolution x dwTexResolution;

		CImage sectorDiffuseImage;

		if (!sectorDiffuseImage.Allocate( dwSectorResolution,dwSectorResolution ))
			return false;

		int flags = ETTG_QUIET|ETTG_NO_TERRAIN_SHADOWS|ETTG_ABGR;

		for (int y = 0; y < dwNumSectors; y++)
			for (int x = 0; x < dwNumSectors; x++)
			{
				CPoint sector(x,y);

				CRect rect(0,0,sectorDiffuseImage.GetWidth(),sectorDiffuseImage.GetHeight());

				if(!texGen.GenerateSectorTexture( sector,rect,flags,sectorDiffuseImage ))
					return false;			// maybe this should be handled better

				// M.M. to take a look at the result of the calculation
				/*				char str[80];
				sprintf(str,"c:\\temp\\tile%d_%d.bmp",x,y);
				CImageUtil::SaveBitmap(str,sectorDiffuseImage);
				*/
				m_cHeightmap.m_TerrainRGBTexture.SetSubImageRGBLayer(x*dwSectorResolution,y*dwSectorResolution,sectorDiffuseImage);
			}
	}

	// extract Detail LayerId info
	GetIEditor()->GetHeightmap()->CalcSurfaceTypes();
	/*
	// M.M. to take a look at the result of the calculation
	uint32 dwExtend = m_cHeightmap.m_TerrainRGBTexture.CalcMinRequiredTextureExtend();
	CImage img;
	img.Allocate(dwExtend,dwExtend);
	m_cHeightmap.m_TerrainRGBTexture.GetSubImageStretched(0,0,1,1,img);
	CImageUtil::SaveBitmap("C:\\temp\\res.bmp",img);
	*/
	// we no longer need the layer mask data
	for(it=m_layers.begin();it!=m_layers.end();++it)
	{
		CLayer *pLayer=*it;

		pLayer->ReleaseMask();
	}

	GetIEditor()->GetHeightmap()->UpdateEngineTerrain(false);

	m_bIsNewTerranTextureSystem=true;

	return true;
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::SetTerrainSize( int resolution,int unitSize )
{
	m_cHeightmap.Resize( resolution,resolution,unitSize );
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::ResetHeightMap()
{
	////////////////////////////////////////////////////////////////////////
	// Reset Heightmap
	////////////////////////////////////////////////////////////////////////
	m_cHeightmap.SetWaterLevel(16); // Default water level.
	m_cHeightmap.Resize(1024,1024,2 );
	m_cHeightmap.SetMaxHeight(1024);
}
//////////////////////////////////////////////////////////////////////////
void CTerrainManager::FreeHeightMapData()
{
	m_cHeightmap.m_TerrainRGBTexture.FreeData();
}
//////////////////////////////////////////////////////////////////////////
bool CTerrainManager::WouldHeightmapSaveSucceed()
{
	return m_cHeightmap.m_TerrainRGBTexture.WouldSaveSucceed();
}
//////////////////////////////////////////////////////////////////////////
CTerrainManager::CTerrainManager()
{

}
//////////////////////////////////////////////////////////////////////////
CTerrainManager::~CTerrainManager()
{

}
//////////////////////////////////////////////////////////////////////////
